/*
 * Copyright (c) United Binary LLC.  All rights reserved.
 * 
 * This code is licensed under the MIT License
 * 
 * SEE: http://harnessit.codeplex.com/license
 * 
 */
#region using ...
using System;
using System.Collections;
using UnitedBinary.UnitTests.Framework;
using UnitedBinary.HarnessIt.SharedTypes;
using UnitedBinary.HarnessIt.UnitTests.Settings;
#endregion

namespace UnitedBinary.HarnessIt.UnitTests.SharedTypes
{
	/// <summary>
	///		Summary description for ClassTests.
	/// </summary>
	[TestClass("Test the Class class.")]
	public class ClassTests
	{
		#region Construction

		public ClassTests()
		{
		}

		#endregion

		#region Test Classes
		private class NonTestClass
		{
		}

		[TestOrder(TestOrderModes.First,7)]
		[TestClass("This class has no methods.")]
		private class EmptyTestClass
		{
		}

		[TestExecutionMode(TestExecutionModes.Reuse)]
		[TestClass("This class will be a test class due to the attribute.")]
		private class ExplicitTestClass
		{
			private void Nothing1() {}
			private void Nothing2() {}
		}

		private class ImplicitTestClass
		{
			[TestMethod("This method will force the class to be a test class.")]
			private void TEST_METHOD(TestMethodRecord tmr)  {}
		}

		[TestExecutionMode(TestExecutionModes.Reuse)]
		[TestOrder(TestOrderModes.First,3)]
		private class OrderedFirstClass
		{
			[TestMethod("This method will force the class to be a test class.")]
			private void TEST_METHOD(TestMethodRecord tmr)  {}
		}

		[TestOrder(TestOrderModes.Last,9)]
		private class OrderedLastClass
		{
			[TestMethod("This method will force the class to be a test class.")]
			private void TEST_METHOD(TestMethodRecord tmr)  {}
		}

		[TestOrder(TestOrderModes.Last)]
		private class OrderedLastDefaultClass
		{
			[TestMethod("This method will force the class to be a test class.")]
			private void TEST_METHOD(TestMethodRecord tmr)  {}
		}
		#endregion

		#region OrderMode Attribute Tests

		[TestMethod("Test the way the OrderMode attributes work our test classes.")]
		public void Attributes_OrderModeTest1(TestMethodRecord tmr)
		{
			Assembly A = new Assembly(UnitTestSettings.BaselineAssemblyPath);
			Class explictTestClass = new Class(typeof(ExplicitTestClass), A);
			Class emptyTestClass = new Class(typeof(EmptyTestClass), A);
			Class implicitTestClass = new Class(typeof(ImplicitTestClass), A);
			Class orderedLastClass = new Class(typeof(OrderedLastClass), A);
			Class orderedLastDefaultClass = new Class(typeof(OrderedLastDefaultClass), A);
			Class orderedFirstClass = new Class(typeof(OrderedFirstClass), A);
			Class nonTestClass = new Class(typeof(NonTestClass), A);

			tmr.RunTest(explictTestClass.OrderMode == TestOrderModes.Neutral, "Explicit test class has neutral order.");
			tmr.RunTest(explictTestClass.SubModeOrder == 100, "Explicit test class has SubModeOrder of 100.");

			tmr.RunTest(emptyTestClass.OrderMode == TestOrderModes.First, "Empty test class has first order.");
			tmr.RunTest(emptyTestClass.SubModeOrder == 7, "Empty test class has SubModeOrder of 7.");

			tmr.RunTest(implicitTestClass.OrderMode == TestOrderModes.Neutral, "Implicit test class has neutral order.");
			tmr.RunTest(implicitTestClass.SubModeOrder == 100, "Implicit test class has SubModeOrder of 100.");

			tmr.RunTest(orderedLastClass.OrderMode == TestOrderModes.Last, "OrderedLast test class has last order.");
			tmr.RunTest(orderedLastClass.SubModeOrder == 9, "OrderedLast test class has SubModeOrder of 9.");

			tmr.RunTest(orderedLastDefaultClass.OrderMode == TestOrderModes.Last, "OrderedLastDefault test class has last order.");
			tmr.RunTest(orderedLastDefaultClass.SubModeOrder == int.MinValue, "OrderedLastDefault test class has SubModeOrder of int.MinValue.");
			
			tmr.RunTest(orderedFirstClass.OrderMode == TestOrderModes.First, "OrderedFirst test class has first order.");
			tmr.RunTest(orderedFirstClass.SubModeOrder == 3, "OrderedFirst test class has SubModeOrder of 3.");
		}
		#endregion

		#region TestClass Attribute Tests

		[TestMethod("Test the way the TestClass attributes work our test classes.")]
		public void Attributes_TestClass1(TestMethodRecord tmr)
		{
			Assembly A = new Assembly(UnitTestSettings.BaselineAssemblyPath);
			Class explictTestClass = new Class(typeof(ExplicitTestClass), A);
			Class emptyTestClass = new Class(typeof(EmptyTestClass), A);
			Class implicitTestClass = new Class(typeof(ImplicitTestClass), A);
			Class orderedLastClass = new Class(typeof(OrderedLastClass), A);
			Class orderedFirstClass = new Class(typeof(OrderedFirstClass), A);
			Class nonTestClass = new Class(typeof(NonTestClass), A);

			string emptyDesc = "This class has no methods.";
			string explicitDesc = "This class will be a test class due to the attribute.";

			tmr.RunTest(explictTestClass.IsTest == true, "Explicit class is a test class.");
			tmr.RunTest(explictTestClass.Description == explicitDesc, "Explicit class description is " + explicitDesc + ".");

			tmr.RunTest(emptyTestClass.IsTest == true, "Empty class is not a test class.");
			tmr.RunTest(emptyTestClass.Description == emptyDesc, "Empty class description is " + emptyDesc + ".");
			
			tmr.RunTest(implicitTestClass.IsTest == false, "Implicit class is not a test class.");
			tmr.RunTest(implicitTestClass.Description == "", "Implicit class description is empty.");

			tmr.RunTest(orderedLastClass.IsTest == false, "OrderedLast class is not a test class.");
			tmr.RunTest(orderedLastClass.Description == "", "OrderedLast class description is empty.");

			tmr.RunTest(orderedFirstClass.IsTest == false, "OrderedFirst class is not a test class.");
			tmr.RunTest(orderedFirstClass.Description == "", "OrderedFirst class description is empty.");

			tmr.RunTest(nonTestClass.IsTest == false, "NonTest class is not a test class.");
			tmr.RunTest(nonTestClass.Description == "", "NonTest class description is empty.");
		}

		#endregion

		#region Construction Tests

		[TestMethod("Test the construction validation with valid arguments.")]
		public void Construction_Validation1(TestMethodRecord tmr)
		{
			Class C = new Class(this.GetType(),new Assembly(UnitTestSettings.BaselineAssemblyPath));
			tmr.RunTest(true, "Construction goes smooth with valid arguments.");
		}

		[TestMethod("Test the construction validation with invalid arguments.")]
		public void Construction_Validation2(TestMethodRecord tmr)
		{
			tmr.RegisterException("Creating class with null arguments causes an exception.",typeof(ArgumentNullException));
			Class C = new Class(this.GetType(),null);
		}

		[TestMethod("Test the construction validation with invalid arguments.")]
		public void Construction_Validation3(TestMethodRecord tmr)
		{
			tmr.RegisterException("Creating class with null arguments causes an exception.",typeof(ArgumentNullException));
			Class C = new Class(null,new Assembly(UnitTestSettings.BaselineAssemblyPath));
		}

		#endregion

		#region Initial Value Tests

		[TestMethod("Test the initial values of the class.")]
		public void Construction_InitialValue(TestMethodRecord tmr)
		{
			Class C = new Class(this.GetType(),new Assembly(UnitTestSettings.BaselineAssemblyPath));
			string desc = "Test the Class class.";
			string name = "UnitedBinary.HarnessIt.UnitTests.SharedTypes.ClassTests";

			tmr.RunTest(C.Active == true, "Class is active.");
			tmr.RunTest(C.ContainsActiveTests == true, "This class contains active tests.");
			tmr.RunTest(C.ContainsTests == true, "This class contains tests.");
			tmr.RunTest(C.Description == desc, "Description = " + desc);
			tmr.RunTest(C.ExecutionMode == TestExecutionModes.Renew, "ExecutionMode is Renew.");
			tmr.RunTest(C.FullName == name, "Full name is " + name);
			tmr.RunTest(C.TestRecord.HasRun == false, "Class has not run tests.");
			tmr.RunTest(C.IsOrdered == false, "Class is not ordered.");
			tmr.RunTest(C.IsTest == true, "Class is a test class.");
			tmr.RunTest(C.Name == "ClassTests", "Short name is ClassTests.");
			tmr.RunTest(C.OrderMode == TestOrderModes.Neutral, "TestOrderMode is neutral.");
			tmr.RunTest(C.SubModeOrder == 100, "SubModeOrder is 100.");
			tmr.RunTest(C.Type == this.GetType(), "Type is the type of this class.");
			tmr.RunTest(C.RunningTime == 0.0, "Running time is zero.");
		}

		#endregion

		#region TestExecutionMode Attribute Tests

		[TestMethod("Test the TestExcecutionMode attribute using our test classes.")]
		public void TestExecutionTests1(TestMethodRecord tmr)
		{
			Assembly A = new Assembly(UnitTestSettings.BaselineAssemblyPath);
			Class explictTestClass = new Class(typeof(ExplicitTestClass), A);
			Class emptyTestClass = new Class(typeof(EmptyTestClass), A);
			Class implicitTestClass = new Class(typeof(ImplicitTestClass), A);
			Class orderedLastClass = new Class(typeof(OrderedLastClass), A);
			Class orderedFirstClass = new Class(typeof(OrderedFirstClass), A);
			Class nonTestClass = new Class(typeof(NonTestClass), A);

			tmr.RunTest(explictTestClass.ExecutionMode == TestExecutionModes.Reuse, "Explicit class uses Renew mode.");
			tmr.RunTest(emptyTestClass.ExecutionMode == TestExecutionModes.Renew, "EmptyTest class uses Renew mode.");
			tmr.RunTest(implicitTestClass.ExecutionMode == TestExecutionModes.Renew, "Implicit class uses Renew mode.");
			tmr.RunTest(orderedLastClass.ExecutionMode == TestExecutionModes.Renew, "OrderedLast class uses Renew mode.");
			tmr.RunTest(orderedFirstClass.ExecutionMode == TestExecutionModes.Reuse, "OrderedFirst class uses Renew mode.");
			tmr.RunTest(nonTestClass.ExecutionMode == TestExecutionModes.Renew, "NonTest class uses Renew mode.");
		}

		[TestMethod("Test the Reuse type recreation of our Class class.")]
		public void TestExecutionTests2(TestMethodRecord tmr)
		{
			Assembly A = new Assembly(UnitTestSettings.BaselineAssemblyPath);
			Class reusedClass = new Class(typeof(ReusedTestClass), A);

			tmr.RunTest(reusedClass.TestRecord.TestCount == 0, "No tests have run yet.");

			object target = Activator.CreateInstance(reusedClass.Type);
			foreach(Method m in reusedClass.Methods)
			{
				m.RunTest(target);
			}

			tmr.RunTest(reusedClass.TestCount == 2, "Both tests ran.");
			tmr.RunTest(reusedClass.TestRecord.Successes == 2, "Both tests succeeded.");
			tmr.RunTest(reusedClass.TestRecord.Failures == 0, "No tests failed.");

			target = Activator.CreateInstance(reusedClass.Type);
			reusedClass.ResetTests();
			foreach(Method m in reusedClass.Methods)
			{
				m.RunTest(target);
			}

			tmr.RunTest(reusedClass.TestCount == 2, "Both tests ran (again).");
			tmr.RunTest(reusedClass.TestRecord.Successes == 2, "Both tests succeeded (again).");
			tmr.RunTest(reusedClass.TestRecord.Failures == 0, "No tests failed (again).");
		}

		#endregion

		#region Custom Class Sorting Tests
		[TestMethod("Test the custom sorting algorithm of our classes.")]
		public void IComparer_Tests(TestMethodRecord tmr)
		{
			Assembly A = new Assembly(UnitTestSettings.BaselineAssemblyPath);
			Class explictTestClass = new Class(typeof(ExplicitTestClass), A);
			Class emptyTestClass = new Class(typeof(EmptyTestClass), A);
			Class implicitTestClass = new Class(typeof(ImplicitTestClass), A);
			Class orderedLastClass = new Class(typeof(OrderedLastClass), A);
			Class orderedFirstClass = new Class(typeof(OrderedFirstClass), A);
			Class nonTestClass = new Class(typeof(NonTestClass), A);

			//
			// I'm sure there are lots of ways to test the sorting, but 
			// sorting in the ArrayList is easy and quick.
			//
			ArrayList list = new ArrayList();
			
			list.Add(explictTestClass);
			list.Add(emptyTestClass);
			list.Add(implicitTestClass);
			list.Add(orderedLastClass);
			list.Add(orderedFirstClass);
			list.Add(nonTestClass);
			
			list.Sort();

			tmr.RunTest(list.IndexOf(orderedFirstClass) == 0, "OrderedFirst class is 0 when sorted.");
			tmr.RunTest(list.IndexOf(emptyTestClass) == 1, "Empty class is 1 when sorted.");

			//
			// These should be somewhere in the middle, doesn't matter what order. So
			// let's just leave these tests off. The necessary condition is implied by the
			// outer three tests.
			//

			//tmr.RunTest(list.IndexOf(nonTestClass) == 2, "NonTest class is 2 when sorted.");
			//tmr.RunTest(list.IndexOf(explictTestClass) == 3, "Explicit class is 3 when sorted.");
			//tmr.RunTest(list.IndexOf(implicitTestClass) == 4, "Implicit class is 4 when sorted.");

			tmr.RunTest(list.IndexOf(orderedLastClass) == 5, "OrderedLast class is 5 (last) when sorted.");
		}
		#endregion

		#region RunningTime tests
		[TestMethod("Test the running time property of the class.")]
		public void RunningTimeTest1(TestMethodRecord tmr)
		{
			Class C = new Class(typeof(ReusedTestClass),new Assembly(UnitTestSettings.BaselineAssemblyPath));

			foreach (Method m in C.Methods)
			{
				if (m.Name == "RunTests1")
				{
					m.RunningTime = 1.25;
				}
				else if(m.Name == "RunTests2")
				{
					m.RunningTime = 2.75;
				}
			}

			tmr.RunTest(C.RunningTime == 4.0, "Running time is now 4 seconds.");
		}
		#endregion
	}
}
